// 打开游戏界面
async function openGame() {
    // 打开设置界面（暂停游戏）
    function openSettings() {
        // 暂停游戏
        config.isGamePaused = true;

        ctrller.settingCtrller = new AbortController();

        // 保存游戏的函数
        function saveGame() {
            // indexedDB存档
            return new Promise((resolve) => {
                indexedDB.open('yunyi', 1).onsuccess = (e) => {
                    const tx = e.target.result.transaction('gameSlot', 'readwrite');

                    // 存档游戏
                    const slotData = tx.objectStore('gameSlot');
                    slotData.put(gameState);

                    tx.oncomplete = () => {
                        config.saved = true;
                        autoCloseAlert('游戏已存档');
                        resolve();
                    };
                };
            });
        }

        // 关闭设置界面按钮
        document.getElementById('close-settings').addEventListener('click', closeSettings, {signal: ctrller.settingCtrller.signal});
        // 退出到开始界面按钮
        document.getElementById('exit-to-menu').addEventListener(
            'click',
            async () => {
                if (!config.saved) {
                    if (await YNAlert('是否保存游戏？')) {
                        await saveGame();
                    }
                }
                closeSettings();
                closeGame();
                openStart();
            },
            {signal: ctrller.settingCtrller.signal}
        );
        // 存档游戏按钮
        document.getElementById('save-game').addEventListener('click', saveGame, {signal: ctrller.settingCtrller.signal});
        // 保存设置按钮、、、、、
        document.getElementById('save-settings').addEventListener(
            'click',
            function() {
                const bgmVolume = document.getElementById('bgm-volume').value;
                const sfxVolume = document.getElementById('sfx-volume').value;
                console.log(`设置已保存 - BGM: ${bgmVolume}%, SFX: ${sfxVolume}%`);
                closeSettings();
            },
            {signal: ctrller.settingCtrller.signal}
        );

        document.getElementById('settings-screen').style.display = 'flex';

        // 关闭设置界面（继续游戏）
        function closeSettings() {
            document.getElementById('settings-screen').style.display = 'none';
            ctrller.settingCtrller.abort();

            // 继续游戏
            config.saved = false;
            config.isGamePaused = false;
            gameLoop();
        }
    }

    // 初始化游戏
    async function initGame() {
        // 工具函数
        // 计算 player底盘中心 在 container 内的相对坐标，单位px
        function playerPosInContainer() {
            // 获取player和容器相对于视口的坐标
            const playerRect = player.element.getBoundingClientRect();
            const containerRect = container.element.getBoundingClientRect();
            
            // 计算 player底盘中心 在 container 内的相对坐标
            const centreX = playerRect.left + player.width / 2 - containerRect.left;
            const centreY = playerRect.top + player.height - containerRect.top;
            
            return { centreX, centreY };
        }

        // 计算 map（左上角） 在 container 内的相对坐标，单位px
        function mapPosInContainer() {
            // 获取视口相对坐标
            const mapRect = map.element.getBoundingClientRect();
            const containerRect = container.element.getBoundingClientRect();
            
            // 计算 map 在 container 内的相对坐标
            const x = mapRect.left - containerRect.left;
            const y = mapRect.top - containerRect.top;
            
            return { x, y };
        }

        // 计算player元素边界坐标值，单位tile
        function playerBorder(direction) {
            if (direction === 'left') {
                return gameState.player.location.x - player.width / (2 * tile.width);
            }
            if (direction === 'right') {
                return gameState.player.location.x + player.width / (2 * tile.width);
            }
            if (direction === 'top') {
                return gameState.player.location.y - player.height / tile.height;
            }
            if (direction === 'bottom') {
                return gameState.player.location.y;
            }
        }

        // 计算player碰撞点坐标值，单位tile
        function obstaclePointsPos(location) {
            const y = {
                top: location.y - config.obstacleY,
                bottom: location.y + config.obstacleY
            };

            const x = [];
            for (let i = 0; playerBorder('left') + i < playerBorder('right'); i++) {
                const pointX = playerBorder('left') + i;
                x.push(pointX);
            }
            x.push(playerBorder('right'));

            return {x, y};
        }

        // 判断是否可以向坐标处移动
        function canMove(x, y) {
            const margin = 3; // 活动范围边界值
            const mapWidth = (!!config?.map?.width) ? config.map.width : gameState.map.base[0].length;
            const mapHeight = (!!config?.map?.height) ? config.map.height : gameState.map.base.length;
            return (
                ( // 可活动边界
                    x >= margin && x <= (mapWidth - 1 - margin) &&
                    y >= margin && y <= (mapHeight - 1 - margin)
                ) &&
                gameState.map.base[y][x] !== 1 &&
                (!isObjectAt(x, y) || !gameState.map.objects[y][x].obstacle)
            );
        }

        // 检测位置是否在玩家附近
        function isNearPlayer(x, y) {
            return (
                x >= gameState.player.location.x - 2 && x <= gameState.player.location.x + 2 &&
                y >= gameState.player.location.y - 2 && y <= gameState.player.location.y + 2
            );
        }

        // 检查某个位置是否有物体
        function isObjectAt(x, y) {
            return !!gameState.map.objects[y] && !!gameState.map.objects[y][x]; // 必须使用!!将undefined转为布尔值
        }

        // 获取某个位置的物体对象
        function getObjectAt(x, y) {
            return gameState.map.objects[y]?.[x];
        }

        // 判断坐标是否在地图内
        function isInMap(x, y) {
            return (
                y >= 0 && gameState.map.base.length > y &&
                x >= 0 && gameState.map.base[y].length > x
            );
        }

        // 随机获取一个无障碍物的空闲陆地坐标
        function freeSpace() {
            const islands = gameState.map.mapRecord.islandRefPoints;
            const island = islands[randomInt(0, islands.length - 1)];
            while (true) {
                const refPoint = island[randomInt(0, island.length - 1)];
                const x = refPoint[0];
                const y = refPoint[1];

                for (let a = -1; a <= 1; a++) {
                    for (let b = -1; b <= 1; b++) {
                        if (
                            isInMap(x + b, y + a) &&
                            canMove(x + b, y + a) &&
                            Math.random() > 0.5 // 更随机
                        ) {
                            return {x: x + b, y: y + a};
                        }
                    }
                }
            }
        }

        // 须与CSS中的类名一致，在渲染地图时用到
        const typeForm = {
            0: 'soil',
            1: 'water-area',
            2: 'stone-base',
            3: 'stone',
            4: 'tree',
        };

        // 生成地图
        function generateMap() {
            const map = {
                width: 50,
                height: 50,
            };

            // 记录地图重要数据
            const mapRecord = {
                stoneBase: [],
                // stoneBase储存格式: [[x, y], [x, y], [x, y], [x, y], ...]
                islandRefPoints: [],
                // islandRefPoints储存格式: [
                //     [[x, y], [x, y], [x, y], [x, y], ...], (岛0的refPoints)
                //     [[x, y], [x, y], [x, y], [x, y], ...], (岛1的refPoints)
                //     [[x, y], [x, y], [x, y], [x, y], ...], (岛2的refPoints)
                //     ...
                // ]
            };

            // 先全部填充为水域
            for (let y = 0; y < map.height; y++) {
                gameState.map.base[y] = [];
                for (let x = 0; x < map.width; x++) {
                    gameState.map.base[y][x] = 1;
                }
            }
            
            const margin = 5; // 生成范围的边距
            const islandsAmount = 3; // 岛屿的大约个数（“大约”是因为岛屿可能重叠）

            // 生成岛屿基准点
            // 终究还是选择了需要考虑 无限循环 的随机生成机制，但很有效
            function generateRefPoints(width, height, margin, minDistance, pointNumber) {
                // 计算有效区域范围
                const minX = margin;
                const maxX = width - 1 - margin;
                const minY = margin;
                const maxY = height - 1 - margin;

                const points = [];
                const maxAttempts = 100 * pointNumber; // 最大尝试次数

                // 生成第一个随机点
                if (pointNumber >= 1) {
                    const x = randomInt(minX, maxX);
                    const y = randomInt(minY, maxY);
                    points.push([x, y]);
                }

                // 生成后续点
                for (let i = 1; i < pointNumber; i++) {
                    let newPoint, valid;

                    // 尝试生成新点
                    for (let attempts = 0; attempts < maxAttempts; attempts++) {
                        // 从现有连接点中选择一个作为基准
                        const basePoint = points[randomInt(0, points.length - 1)];
                        
                        // 在基准点周围生成候选点
                        const angle = Math.random() * Math.PI * 2;
                        const x = Math.round(basePoint[0] + minDistance * Math.cos(angle));
                        const y = Math.round(basePoint[1] + minDistance * Math.sin(angle));
                        
                        // 检查是否在有效范围内
                        if (x < minX || x > maxX || y < minY || y > maxY) continue;
                        
                        newPoint = [x, y];
                        
                        // 检查与所有现有点的距离
                        valid = true; // 在每次循环里定义valid，避免上一次循环valid值影响本次循环
                        for (const p of points) {
                            const dx = p[0] - x;
                            const dy = p[1] - y;
                            const distance = Math.sqrt(dx * dx + dy * dy);
                            // 距离小于最小距离则不有效
                            if (distance < minDistance) {
                                valid = false;
                                break;
                            }
                        }
                        
                        // 有效
                        if (valid) break;
                    }

                    // 跳出尝试循环有两种可能：坐标有效、循环超过最大尝试次数
                    if (valid) {
                        points.push(newPoint);
                    } else { // 循环超过最大尝试次数但仍无法找到有效基准点
                        alert('ERROR!!!\n无法生成地图基准点\n请重新创建世界');
                        break;
                    }
                }

                return points;
            }
            // 随机生成岛屿基准点
            for (let i = 0; i < islandsAmount; i++) {
                referencePoints = generateRefPoints(map.width, map.height, margin, 2, randomInt(5, 50));
                // 记录基准点
                mapRecord.islandRefPoints.push(referencePoints);
            }

            // 根据基准点生成土壤
            mapRecord.islandRefPoints.forEach((refPoints) => {
                refPoints.forEach((pos) => {
                    const x = pos[0];
                    const y = pos[1];
                    for (let a = -1; a <= 1; a++) { // 九宫格
                        for (let b = -1; b <= 1; b++) {
                            if (
                                isInMap(x + b, y + a) &&
                                Math.random() > 0.05 // 使可能有土壤缺失
                            ) {
                                gameState.map.base[y + a][x + b] = 0;
                            }
                        }
                    }
                });
            });

            // 在岛屿上随机生成石基
            mapRecord.islandRefPoints.forEach((refPoints) => {
                // 随机石基区大致个数
                const stoneBaseSize = randomInt(0, Math.floor(refPoints.length / 3));

                for (let i = 0; i < stoneBaseSize; i++) {
                    const x = refPoints[i][0];
                    const y = refPoints[i][1];
                    // 九宫格
                    for (let a = -1; a <= 1; a++) {
                        for (let b = -1; b <= 1; b++) {
                            if (
                                isInMap(x + b, y + a) &&
                                gameState.map.base[y + a][x + b] === 0 && // 只在土壤处生成，同时避免同一坐标重复生成石基
                                Math.random() > 0.3 // 使可能有石基缺失
                            ) {
                                gameState.map.base[y + a][x + b] = 2;
                                // 记录石基位置
                                mapRecord.stoneBase.push([x + b, y + a]);
                            }
                        }
                    }
                }
            });

            // 在每个岛上添加几棵树
            mapRecord.islandRefPoints.forEach((refPoints) => {
                refPoints.forEach((pos) => {
                    const x = pos[0];
                    const y = pos[1];

                    // 只在土壤上生成树
                    if (gameState.map.base[y][x] === 0 && Math.random() > 0.5) {
                        // 避免gameState.map.objects[y]未定义
                        if (!gameState.map.objects[y]) {
                            gameState.map.objects[y] = {};
                        }
                        gameState.map.objects[y][x] = {
                            type: 4,
                            obstacle: true,
                        };
                    }
                });
            });

            // 在石基上生成石头
            mapRecord.stoneBase.forEach((pos) => {
                const x = pos[0];
                const y = pos[1];

                // 避免gameState.map.objects[y]未定义
                if (!gameState.map.objects[y]) {
                    gameState.map.objects[y] = {};
                }
                gameState.map.objects[y][x] = {
                    type: 3,
                    obstacle: true,
                };
            });

            gameState.map.mapRecord = mapRecord;
        }

        // 渲染地图底层
        function renderMapBase() {
            for (let y = 0; y < config.map.height; y++) {
                tile.base[y] = [];
                for (let x = 0; x < config.map.width; x++) {
                    tile.base[y][x] = document.createElement('div');
                    tile.base[y][x].className = 'tile';
                    tile.base[y][x].style.left = x * tile.width + 'px';
                    tile.base[y][x].style.top = y * tile.height + 'px';
                    // tile.base[y][x].textContent = `${x}, ${y}`; // 调试-----
                    tile.base[y][x].classList.add(typeForm[gameState.map.base[y][x]]);
                    map.element.appendChild(tile.base[y][x]);
                }
            }
        }

        // 渲染物体
        function renderMapSurface() {
            for (const y in gameState.map.objects) {
                tile.objects[y] = {};
                for (const x in gameState.map.objects[y]) {
                    tile.objects[y][x] = document.createElement('div');
                    tile.objects[y][x].className = `tile ${typeForm[gameState.map.objects[y][x].type]}`;
                    tile.objects[y][x].style.left = x * tile.width + 'px';
                    tile.objects[y][x].style.top = y * tile.height + 'px';
                    // tile.objects[y][x].textContent = `${x}, ${y}`; // 调试-----
                    map.element.appendChild(tile.objects[y][x]);
                }
            }
        }

        // 更新UI
        function updateUI() {
            document.getElementById('health').textContent = gameState.player.health;
        }

        // 注册监听器，实时监控keys中已注册的按键变化
        function setKeysListener() {
            document.addEventListener('keydown',
                (e) => {
                    if (keys.hasOwnProperty(e.code)) { // 避免意外的按键失效
                        e.preventDefault();
                        keys[e.code] = true;
                    }
                },
                {signal: ctrller.gameCtrller.signal}
            );
            
            document.addEventListener('keyup',
                (e) => {
                    if (keys.hasOwnProperty(e.code)) {
                        e.preventDefault();
                        keys[e.code] = false;
                    }
                },
                {signal: ctrller.gameCtrller.signal}
            );
        }

        // 设置按钮
        function setGameButton() {
            // 设置暂停按钮
            document.getElementById('pauseGame').addEventListener(
                'click',
                function() {
                    openSettings();
                },
                {signal: ctrller.gameCtrller.signal}
            );
        }

        // 角色跳跃、、、、、
        function jump() {

        }

        // 砍树、、、、、
        function cutTree() {
            
        }

        // 切换player皮肤
        function changePlayerSkin(skinUrl) {
            // 排除不必要的皮肤切换
            if (player.element.style.backgroundImage === skinUrl) {
                return;
            }

            // 传输图片总宽
            const img = new Image();
            img.onload = function() {
                document.documentElement.style.setProperty('--img-width', this.naturalWidth);
            };
            img.src = (skinUrl).slice(5, -2);

            // 设置皮肤
            player.element.style.backgroundImage = skinUrl;
        }

        // 向CSS中传入数据
        function sendDataToCSS() {
            // 直接显示保存时的视角后再加入过渡效果
            map.element.style.transform = `translate(${gameState.map.position.x}px, ${gameState.map.position.y}px)`;
            player.element.style.transform = `translate(${gameState.map.position.x}px, ${gameState.map.position.y}px)`;
            void map.element.offsetWidth;
            const transition = 'transform 1s cubic-bezier(0.2,0.2,0.5,1)';
            map.element.style.transition = transition;
            player.element.style.transition = transition;
            // 其他数据
            document.documentElement.style.setProperty('--tile-width', `${tile.width}px`);
            document.documentElement.style.setProperty('--tile-height', `${tile.height}px`);
            document.documentElement.style.setProperty('--frame-width', player.width);
            player.element.style.height = `${player.height}px`;
            map.element.style.width = `${map.width}px`;
            map.element.style.height = `${map.height}px`;
        }

        // 渲染玩家和移动
        function renderPlayer() {
            // 使用playerBorder将gameState.player.x/y转换为left/top，若需修改，应该修改playerBorder
            player.element.style.left = (playerBorder('left') * tile.width) + 'px';
            player.element.style.top = (playerBorder('top') * tile.height) + 'px';
        }

        // 地图移动
        function mapMove() {
            const { centreX: playerx, centreY: playery } = playerPosInContainer();

            const limSize = 2; // 地图需移动的临界值，单位tile
            const rightX = (container.width / tile.width - limSize) * tile.width;
            const leftX = limSize * tile.width;
            const bottomY = (container.height / tile.height - limSize) * tile.height;
            const topY = limSize * tile.height;

            const dispX = Math.min(400, container.width / 2 - limSize * tile.width);
            const dispY = Math.min(400, container.height / 2 - limSize * tile.height);

            // 当角色到容器边界时
            let move = false;
            if (playerx > rightX) { // 地图左移
                gameState.map.position.x -= dispX;
                move = true;
            }
            if (playerx < leftX) { // 地图右移
                gameState.map.position.x += dispX;
                move = true;
            }
            if (playery > bottomY) { // 地图上移
                gameState.map.position.y -= dispY;
                move = true;
            }
            if (playery < topY) { // 地图下移
                gameState.map.position.y += dispY;
                move = true;
            }
            if (move) {
                map.element.style.transform = `translate(${gameState.map.position.x}px, ${gameState.map.position.y}px)`;
                player.element.style.transform = `translate(${gameState.map.position.x}px, ${gameState.map.position.y}px)`;
            }
        }
        const throttledMapMove = throttle(mapMove, 200);

        // 创建需循环的函数，用对象装载函数，避免每次循环时重新创建
        // 不在gameLoop中使用闭包是因为函数所需的变量在initGame里，而且这样可以避免闭包占用内存
        funInLoop = {
            // 角色移动
            playerMove() {
                let moveX = false;
                let moveY = false;
                let dispX = 0;
                let dispY = 0;
                const disp = config.player.speed / 100;
                
                if (keys.KeyW) {
                    dispY -= disp;
                    moveY = true;
                    if (keys.KeyH) {dispY -= disp;} // 加速
                }
                if (keys.KeyS) {
                    dispY += disp;
                    moveY = true;
                    if (keys.KeyH) {dispY += disp;}
                }
                if (keys.KeyA) {
                    dispX -= disp;
                    moveX = true;
                    if (keys.KeyH) {dispX -= disp;}
                }
                if (keys.KeyD) {
                    dispX += disp;
                    moveX = true;
                    if (keys.KeyH) {dispX += disp;}
                }

                // 检查左右侧碰撞体
                if (moveX) {
                    // 计算碰撞点坐标
                    obstaclePos = obstaclePointsPos(gameState.player.location);

                    const newLeft = Math.floor(obstaclePos.x[0] + dispX);
                    const newRight = Math.floor(obstaclePos.x.at(-1) + dispX);
                    const initialTop = Math.floor(obstaclePos.y.top);
                    const initialBottom = Math.floor(obstaclePos.y.bottom);

                    if (
                        canMove(newLeft, initialTop) &&
                        canMove(newLeft, initialBottom) &&
                        canMove(newRight, initialTop) &&
                        canMove(newRight, initialBottom)
                    ) {
                        gameState.player.location.x += dispX;
                    } else { // 到碰撞体附近，贴近碰撞体
                        if (dispX < 0) { // 向左移动
                            gameState.player.location.x = Math.floor(obstaclePos.x[0]) + 0.01 + player.width / (2 * tile.width);
                        }
                        if (dispX > 0) { // 向右移动
                            gameState.player.location.x = Math.ceil(obstaclePos.x.at(-1)) - 0.01 - player.width / (2 * tile.width);
                        }
                    }
                }
                // 检查上下侧碰撞体
                if (moveY) {
                    // 计算碰撞点坐标
                    obstaclePos = obstaclePointsPos(gameState.player.location);

                    const newTop = Math.floor(obstaclePos.y.top + dispY);
                    const newBottom = Math.floor(obstaclePos.y.bottom + dispY);

                    let normalMove = true;
                    obstaclePos.x.forEach((x) => {
                        const initialX = Math.floor(x);
                        normalMove = normalMove && (
                            canMove(initialX, newTop) &&
                            canMove(initialX, newBottom)
                        );
                    });

                    if (normalMove) {
                        gameState.player.location.y += dispY;
                    } else { // 到碰撞体附近，贴近碰撞体
                        if (dispY < 0) { // 向上移动
                            gameState.player.location.y = Math.floor(obstaclePos.y.top) + 0.01 + config.obstacleY;
                        }
                        if (dispY > 0) { // 向下移动
                            gameState.player.location.y = Math.ceil(obstaclePos.y.bottom) - 0.01 - config.obstacleY;
                        }
                    } 
                }
                if (moveX || moveY){
                    renderPlayer();
                    throttledMapMove();
                }
            },

            // player移动时的皮肤切换逻辑
            playerSkinChanging() {
                const currentMoveKeys = Object.keys(keys).filter(key => moveKeys.includes(key) && keys[key] === true);

                if (currentMoveKeys.length !== 0) { // 为了代码的简明性，避免使用类似 !!currentKeys.length 的难以理解的代码
                    // 切换行走皮肤
                    switch (currentMoveKeys[0]) {
                        case 'KeyW':
                            changePlayerSkin(gameState.player.skin.backMove);
                            break;
                        case 'KeyS':
                            changePlayerSkin(gameState.player.skin.frontMove);
                            break;
                        case 'KeyA':
                            changePlayerSkin(gameState.player.skin.leftMove);
                            break;
                        case 'KeyD':
                            changePlayerSkin(gameState.player.skin.rightMove);
                            break;
                    }
                } else if (config.lastMoveKeys.length !== 0) {
                    // 切换站立皮肤
                    switch (config.lastMoveKeys[0]) {
                        case 'KeyW':
                            changePlayerSkin(gameState.player.skin.back);
                            break;
                        case 'KeyS':
                            changePlayerSkin(gameState.player.skin.front);
                            break;
                        case 'KeyA':
                            changePlayerSkin(gameState.player.skin.left);
                            break;
                        case 'KeyD':
                            changePlayerSkin(gameState.player.skin.right);
                            break;
                    }
                }
                config.lastMoveKeys = currentMoveKeys;
            },

            // 无障碍角色移动-----
            cheatingPlayerMove() {
                let moveX = false;
                let moveY = false;
                let dispX = 0;
                let dispY = 0;
                const disp = config.player.speed / 100;
                
                if (keys.KeyW) {
                    dispY -= disp;
                    moveY = true;
                    if (keys.KeyH) {dispY -= disp;} // 加速
                }
                if (keys.KeyS) {
                    dispY += disp;
                    moveY = true;
                    if (keys.KeyH) {dispY += disp;}
                }
                if (keys.KeyA) {
                    dispX -= disp;
                    moveX = true;
                    if (keys.KeyH) {dispX -= disp;}
                }
                if (keys.KeyD) {
                    dispX += disp;
                    moveX = true;
                    if (keys.KeyH) {dispX += disp;}
                }

                if (moveX) {
                    gameState.player.location.x += dispX;
                }
                if (moveY) {
                    gameState.player.location.y += dispY;
                }

                if (moveX || moveY){
                    renderPlayer();
                    throttledMapMove();
                }
            },
        };

        // 加载游戏
        function loadGame() {
            // const difficulty = document.querySelector('input[name="difficulty"]:checked').value;

            sendDataToCSS();
            renderMapBase();
            renderMapSurface();
            changePlayerSkin(gameState.player.skin.front);
            renderPlayer();
            setGameButton();
            setKeysListener();
            updateUI();
        }

        // 游戏配置，供加载游戏使用
        config = {
            map: {
                width: null, // 这里不定义，只做结构参考
                height: null,
            },
            tile: {
                width: 50, // 单位px
                height: 50,
            },
            player: {
                width: 45, // 单位px
                height: 75,
                speed: 7,
            },
            obstacleY: 0.4, // 单位tile
            isGamePaused: false,
            lastMoveKeys: Object.keys(keys).filter(key => moveKeys.includes(key) && keys[key] === true),
            saved: false,
        };

        const container = {
            element: document.getElementById('game-container'),
            width: document.getElementById('game-container').getBoundingClientRect().width,
            height: document.getElementById('game-container').getBoundingClientRect().height
        };
        const map = {
            element: document.getElementById('game-map'),
            width: config.map.width * config.tile.width,
            height: config.map.height * config.tile.height
        };
        const player = {
            element: document.getElementById('player'),
            width: config.player.width,
            height: config.player.height
        };
        const tile = {
            base: [],
            objects: {},
            width: config.tile.width,
            height: config.tile.height,
        }

        // 创建或调用游戏存档
        if (asset.openWay === 'new') {
            // 初始化游戏状态，供存档使用
            gameState = {
                id: document.getElementById('slot-name').value,
                player: {
                    location: {
                        x: null, // 单位tile，这里不定义，只做结构参考
                        y: null,
                    },
                    health: 100,
                    skin: skinsArr[document.querySelector('.skin-option.selected').dataset.order],
                },
                map: {
                    position: { // 地图（与玩家）偏移量
                        x: null, // 单位px
                        y: null,
                    },
                    base: [],
                    objects: {},
                    mapRecord: null, // 在generateMap函数中查看结构
                },
            };
            generateMap();

            // 角色出生位置只能在地图生成后决定
            const freeLocation = freeSpace();
            gameState.player.location = {
                x: freeLocation.x + 0.5,
                y: freeLocation.y + 0.5,
            }
            gameState.map.position = {
                x: -gameState.player.location.x * config.tile.width + container.width / 2,
                y: -gameState.player.location.y * config.tile.height + container.height / 2,
            }

        } else if (asset.openWay === 'old') {
            // 获取游戏存档
            await new Promise((resolve) => {
                indexedDB.open('yunyi', 1).onsuccess = (e) => {
                    const slotData = e.target.result.transaction('gameSlot', 'readonly').objectStore('gameSlot');

                    slotData.get(asset.currentSlot).onsuccess = (e) => {
                        gameState = e.target.result;
                        resolve();
                    };
                };
            });
        }

        // config和gameState里的变量决定顺序各有先后，所以有些变量只得在这里定义
        config.map = {
            width: gameState.map.base[0].length,
            height: gameState.map.base.length,
        };
        
        loadGame();
    }

    // 游戏循环，在暂停游戏后再继续时需要用到
    function gameLoop() {
        const gameAnimation = requestAnimationFrame(gameLoop); // 注册下一帧
        if (
            document.getElementById('game-container').style.display === 'none' || // 关闭游戏界面时退出循环
            config.isGamePaused // 暂停游戏时退出循环
        ) {
            cancelAnimationFrame(gameAnimation); // 取消下一帧
            return; // 跳出当前帧
        }

        funInLoop.playerSkinChanging();
        // funInLoop.playerMove();
        funInLoop.cheatingPlayerMove();
    }

    document.getElementById('game-container').style.display = 'block';
    ctrller.gameCtrller = new AbortController();
    await initGame(); // 获取游戏存档是异步操作
    gameLoop();

    // 关闭游戏界面
    function closeGame() {
        // 移除监听器
        ctrller.gameCtrller.abort();
        // 清空地图
        document.getElementById('game-map').innerHTML = '';
        // 清除地图移动的过渡效果
        document.getElementById('game-map').style.transition = 'none';
        document.getElementById('player').style.transition = 'none';
        // 关闭界面
        document.getElementById('game-container').style.display = 'none';
        // 释放内存
        config = null;
        gameState = null;
        funInLoop = null;
    }
}